﻿//---------------------------------------------------------------------------------
// Copyright 2011 Microsoft Corporation
// Licensed under the Microsoft Public License (MS-PL) (the "License"); 
// You may not use this file except in compliance with the License. 
// You may obtain a copy of the License at http://www.opensource.org/licenses/ms-pl.html
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR 
// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, 
// INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR 
// CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, 
// MERCHANTABLITY OR NON-INFRINGEMENT. 
// See the Microsoft Public License (MS-PL) for the specific language governing 
// permissions and limitations under the License.
//---------------------------------------------------------------------------------

using System;
using System.ServiceModel;
using System.ServiceModel.Dispatcher;
using Microsoft.Telco.Web.Contracts;

namespace Microsoft.Telco.Web.Services.ServiceModel
{
    /// <summary>
    /// Wrapper implementation of IOperationInvoker that logs exceptions during service method invoking instead of raising up to the WCF fault handling system.
    /// </summary>
    internal class WrappedMethodInvoker : IOperationInvoker
    {

        IOperationInvoker innerInvoker = null;

        /// <summary>
        /// Initializes a new instance of the <see cref="WrappedMethodInvoker"/> class.
        /// </summary>
        /// <param name="innerInvoker">The inner invoker.</param>
        public WrappedMethodInvoker(IOperationInvoker innerInvoker)
        {

            if (innerInvoker == null)
            {
                throw new ArgumentNullException("innerInvoker");
            }

            this.innerInvoker = innerInvoker;
        }

        #region IOperationInvoker Members

        /// <summary>
        /// Returns an <see cref="T:System.Array"/> of parameter objects.
        /// </summary>
        /// <returns>
        /// The parameters that are to be used as arguments to the operation.
        /// </returns>
        public object[] AllocateInputs()
        {
            return this.innerInvoker.AllocateInputs();
        }

        /// <summary>
        /// Returns an object and a set of output objects from an instance and set of input objects.
        /// </summary>
        /// <param name="instance">The object to be invoked.</param>
        /// <param name="inputs">The inputs to the method.</param>
        /// <param name="outputs">The outputs from the method.</param>
        /// <returns>The return value.</returns>
        public object Invoke(object instance, object[] inputs, out object[] outputs)
        {

            // satisfy need to set outputs
            outputs = EmptyArray.Instance;

            try
            {
                return this.innerInvoker.Invoke(instance, inputs, out outputs);
            }
            catch (FaultException)
            {
                // For any faults thrown by the client, pass through
                throw;
            }
            catch (Exception ex)
            {
                throw new FaultException<TelcoFault>(new TelcoFault(ex.Message));
            }
        }

        /// <summary>
        /// An asynchronous implementation of the <see cref="M:System.ServiceModel.Dispatcher.IOperationInvoker.Invoke(System.Object,System.Object[],System.Object[]@)"/> method.
        /// </summary>
        /// <param name="instance">The object to be invoked.</param>
        /// <param name="inputs">The inputs to the method.</param>
        /// <param name="callback">The asynchronous callback object.</param>
        /// <param name="state">Associated state data.</param>
        /// <returns>
        /// A <see cref="T:System.IAsyncResult"/> used to complete the asynchronous call.
        /// </returns>
        public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
        {

            try
            {
                return this.innerInvoker.InvokeBegin(instance, inputs, callback, state);
            }
            catch (FaultException)
            {
                // For any faults thrown by the client, pass through
                throw;
            }
            catch (Exception ex)
            {
                throw new FaultException<TelcoFault>(new TelcoFault(ex.Message));
            }

        }

        /// <summary>
        /// The asynchronous end method.
        /// </summary>
        /// <param name="instance">The object invoked.</param>
        /// <param name="outputs">The outputs from the method.</param>
        /// <param name="result">The <see cref="T:System.IAsyncResult"/> object.</param>
        /// <returns>The return value.</returns>
        public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
        {

            // satisfy need to set outputs
            outputs = EmptyArray.Instance;

            try
            {
                return this.innerInvoker.InvokeEnd(instance, out outputs, result);
            }

            catch (FaultException)
            {
                // For any faults thrown by the client, pass through
                throw;
            }
            catch (Exception ex)
            {
                throw new FaultException<TelcoFault>(new TelcoFault(ex.Message));
            }

        }

        /// <summary>
        /// Gets a value that specifies whether the <see cref="M:System.ServiceModel.Dispatcher.IOperationInvoker.Invoke(System.Object,System.Object[],System.Object[]@)"/> or <see cref="M:System.ServiceModel.Dispatcher.IOperationInvoker.InvokeBegin(System.Object,System.Object[],System.AsyncCallback,System.Object)"/> method is called by the dispatcher.
        /// </summary>
        /// <value></value>
        /// <returns>true if the dispatcher invokes the synchronous operation; otherwise, false.</returns>
        public bool IsSynchronous
        {
            get { return this.innerInvoker.IsSynchronous; }
        }

        #endregion

    }
}
